// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef COMPONENTS_CHROMEOS_CAMERA_MJPEG_DECODE_ACCELERATOR_H_
#define COMPONENTS_CHROMEOS_CAMERA_MJPEG_DECODE_ACCELERATOR_H_

#include <stddef.h>
#include <stdint.h>
#include <sys/types.h>

#include "base/callback.h"
#include "base/files/scoped_file.h"
#include "base/memory/scoped_refptr.h"
#include "media/base/bitstream_buffer.h"

namespace media {
class VideoFrame;
}

namespace chromeos_camera {

// MJPEG decoder interface.
// The input are JPEG images including headers (Huffman tables may be omitted).
// The decoder will convert to the output color format if the input color format
// or subsampling does not match that and if it is capable of doing so. The
// client is responsible for allocating buffers and keeps the ownership of them.
// The intended use case of this interface is decoding MJPEG images coming
// from camera capture. It can also be used for normal still JPEG image
// decoding, but normal JPEG images may use more JPEG features that may not be
// supported by a particular accelerator implementation and/or platform.
class MjpegDecodeAccelerator {
 public:
  // Callback for JPEG decoder initialization.
  typedef base::OnceCallback<void(bool success)> InitCB;

  static const int32_t kInvalidTaskId = -1;

  // Enumeration of decode errors generated by NotifyError callback. These
  // values are persisted to logs. Entries should not be renumbered and numeric
  // values should never be reused.
  enum Error {
    // No error. Decode succeeded.
    NO_ERRORS = 0,
    // Invalid argument was passed to an API method, e.g. the output buffer is
    // too small, JPEG width/height are too big for JDA.
    INVALID_ARGUMENT = 1,
    // Encoded input is unreadable, e.g. failed to map on another process.
    UNREADABLE_INPUT = 2,
    // Failed to parse compressed JPEG picture.
    PARSE_JPEG_FAILED = 3,
    // Failed to decode JPEG due to unsupported JPEG features, such as profiles,
    // coding mode, or color formats.
    UNSUPPORTED_JPEG = 4,
    // A fatal failure occurred in the GPU process layer or one of its
    // dependencies. Examples of such failures include hardware failures,
    // driver failures, library failures, browser programming errors, and so
    // on. Client is responsible for destroying JDA after receiving this.
    PLATFORM_FAILURE = 5,
    // Largest used enum. This should be adjusted when new errors are added.
    MJDA_ERROR_CODE_MAX = PLATFORM_FAILURE,
  };

  class Client {
   public:
    // Callback called after each successful Decode().
    // Parameters:
    //  |task_id| is the id passed to Decode() call.
    virtual void VideoFrameReady(int32_t task_id) = 0;

    // Callback to notify errors. Client is responsible for destroying JDA when
    // receiving a fatal error, i.e. PLATFORM_FAILURE. For other errors, client
    // is informed about the buffer that failed to decode and may continue
    // using the same instance of JDA.
    // Parameters:
    //  |error| is the error code.
    //  |task_id| is the id passed to Decode() call that resulted in the
    //  recoverable error. For PLATFORM_FAILURE, |task_id| may be
    //  |kInvalidTaskId| if the error was not related to any particular buffer
    //  being processed.
    virtual void NotifyError(int32_t task_id, Error error) = 0;

   protected:
    virtual ~Client() {}
  };

  // Destroys the decoder: all pending inputs are dropped immediately. This
  // call may asynchronously free system resources, but its client-visible
  // effects are synchronous. After destructor returns, no more callbacks
  // will be made on the client.
  virtual ~MjpegDecodeAccelerator() = 0;

  // Initializes the MJPEG decoder. Should be called once per decoder
  // construction. This call is asynchronous and executes |init_cb| upon
  // completion. Parameters:
  //  |client| is the Client interface for decode callback. The provided
  //  pointer must be valid until destructor is called.
  //  |init_cb| is the MJPEG decoder initialization status report callback.
  //
  // |init_cb| is called on the same thread as InitializeAsync() and can
  // potentially be called even after the MjpegDecodeAccelerator destructor.
  virtual void InitializeAsync(Client* client, InitCB init_cb) {}

  // Decodes the given bitstream buffer that contains one JPEG frame. It
  // supports at least baseline encoding defined in JPEG ISO/IEC 10918-1. The
  // decoder will convert the output to |video_frame->format()| or return
  // PLATFORM_FAILURE if it cannot convert.
  // Parameters:
  //  |bitstream_buffer| contains encoded JPEG frame.
  //  |video_frame| contains an allocated video frame for the output, backed
  //  with an UnsafeSharedMemoryRegion or DMA buffer.
  //
  //  Client is responsible for filling the |video_frame->coded_size()|,
  //  |video_frame->visible_rect()|, and allocating its backing buffer. For
  //  unsafe shared memory backed VideoFrames, I420 and NV12 formats are
  //  supported. For DMA-buf backed VideoFrames, the supported formats depend on
  //  the underlying hardware implementation. After decode completes, the
  //  decoded JPEG frame will be filled into the |video_frame|. Ownership of the
  //  |bitstream_buffer| and |video_frame| remains with the client. The client
  //  is not allowed to deallocate them before VideoFrameReady or NotifyError()
  //  is invoked for given id of |bitstream_buffer|, or destructor returns.
  virtual void Decode(media::BitstreamBuffer bitstream_buffer,
                      scoped_refptr<media::VideoFrame> video_frame) = 0;

  // The same as above but the JPEG image is stored in a DMA buffer.
  // Parameters:
  //  |src_dmabuf_fd| contains encoded JPEG frame.
  //  |src_size| is the size of the JPEG frame.
  //  |src_offset| is the offset at which the JPEG data starts.
  //  |dst_frame| contains an allocated video frame for the output, backed with
  //  an UnsafeSharedMemoryRegion or DMA buffer.
  virtual void Decode(int32_t task_id,
                      base::ScopedFD src_dmabuf_fd,
                      size_t src_size,
                      off_t src_offset,
                      scoped_refptr<media::VideoFrame> dst_frame) = 0;

  // Returns true when the JPEG decoder is supported. This can be called before
  // Initialize().
  virtual bool IsSupported() = 0;
};

}  // namespace chromeos_camera

#endif  // COMPONENTS_CHROMEOS_CAMERA_MJPEG_DECODE_ACCELERATOR_H_
